package com.jzo2o.market.handler;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.jzo2o.api.customer.CommonUserApi;
import com.jzo2o.api.customer.dto.response.CommonUserResDTO;
import com.jzo2o.common.expcetions.CommonException;
import com.jzo2o.common.utils.BeanUtils;
import com.jzo2o.common.utils.JsonUtils;
import com.jzo2o.common.utils.NumberUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.market.model.domain.Activity;
import com.jzo2o.market.model.domain.Coupon;
import com.jzo2o.market.model.dto.response.SeizeCouponInfoResDTO;
import com.jzo2o.market.service.IActivityService;
import com.jzo2o.market.service.ICouponService;
import com.jzo2o.redis.model.SyncMessage;
import com.jzo2o.redis.sync.SyncManager;
import com.jzo2o.redis.utils.RedisSyncQueueUtils;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

import static com.jzo2o.market.constants.RedisConstants.RedisKey.*;

@Component
@Slf4j
public class XxlJobHandler {

    @Resource
    private SyncManager syncManager;

    @Resource
    private IActivityService activityService;

    @Resource
    private ICouponService couponService;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private CommonUserApi commonUserApi;

    @Resource(name = "syncThreadPool")
    private ThreadPoolExecutor threadPoolExecutor;


    /**
     * 活动状态修改，
     * 1.活动进行中状态修改
     * 2.活动已失效状态修改
     * 每分钟执行一次
     */
    @XxlJob("updateActivityStatus")
    public void updateActivitySatus(){
        log.info("定时修改活动状态...");
        try {
            activityService.updateStatus();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 已领取优惠券自动过期任务
     * 每小时执行一次
     */
    @XxlJob("processExpireCoupon")
    public void processExpireCoupon() {
        log.info("已领取优惠券自动过期任务...");
        try {
            couponService.processExpireCoupon();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 活动信息定时预热任务
     */
    @XxlJob("activityPreheat")
    public void activityPreheat() {
        log.info("活动信息定时预热任务...");
        try {
            activityService.preHeat();
        } catch (Exception e) {

        }
    }

    /**
     * 抢券结果同步任务
     */
    @XxlJob("seizeCouponSyncJob")
    public void seizeCouponSyncJob() {
        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.execute(new RunnableSimple(i));
        }
    }

    public class RunnableSimple implements Runnable{

        private int index;

        public RunnableSimple(int index) {
            this.index = index;
        }

        @Override
        public void run() {
            // 从抢券同步队列中读取数据
            String key = RedisSyncQueueUtils.getQueueRedisKey(COUPON_SEIZE_SYNC_QUEUE_NAME, index);
            ScanOptions options = ScanOptions.scanOptions().count(10).build();
            Cursor<Map.Entry<String, Long>> scan = null;
            try {
                // 查询抢券同步队列缓存信息，每次查询10条count(10)
                scan = redisTemplate.opsForHash().scan(key, options);
                // stream流对数据进行处理
                List<SyncMessage> collect = scan.stream().map(item -> {
                    return (SyncMessage) SyncMessage.builder()
                            .key(item.getKey())
                            .value(item.getValue())
                            .build();
                }).collect(Collectors.toList());
                log.info("处理完的数据为 {}", collect);

                // 活动库存信息同步，同步抢券用户的优惠券信息
                synchronization(collect);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if(scan != null) {
                    scan.close();
                }
            }
        }

        @Transactional(rollbackFor = Exception.class)
        public void synchronization(List<SyncMessage> collect) {
            collect.forEach(item -> {
                long actId = NumberUtils.parseLong(item.getValue().toString());
                // 更新活动库存
                String stockKey = String.format(COUPON_RESOURCE_STOCK, actId % 10);
                // 活动库存信息
                Map<String, Long> entries = redisTemplate.opsForHash().entries(stockKey);
                Map.Entry<String, Long> longLongEntry = entries.entrySet().stream().findFirst().orElse(null);
                // 数据转换
                // 将缓存中最新的库存更新到mysql中
                boolean update = activityService.update(new LambdaUpdateWrapper<Activity>()
                        .eq(Activity::getId, NumberUtils.parseLong(longLongEntry.getKey()))
                        .set(Activity::getStockNum, longLongEntry.getValue()));
                if(!update) {
                    throw new CommonException("同步活动库存更新失败");
                }

                // 同步当前抢券用户的优惠券信息
                // 查询出活动信息
                String json = redisTemplate.opsForValue().get(ACTIVITY_CACHE_LIST).toString();
                // 数据转换
                List<SeizeCouponInfoResDTO> dto = JsonUtils.toList(json, SeizeCouponInfoResDTO.class);
                // 对数据进行筛选，筛选出当前同步的活动信息
                SeizeCouponInfoResDTO seizeCouponInfoResDTO =
                        dto.stream().filter(v -> v.getId() == actId).findFirst().orElse(null);
                if(ObjectUtils.isNull(seizeCouponInfoResDTO)) {
                    throw new CommonException("活动异常，请重试");
                }

                // 获取用户信息
                CommonUserResDTO userInfo = commonUserApi.findById(NumberUtils.parseLong(item.getKey()));
                Coupon coupon = BeanUtils.toBean(seizeCouponInfoResDTO, Coupon.class);
                coupon.setUserId(userInfo.getId());
                coupon.setUserName(userInfo.getNickname());
                coupon.setUserPhone(userInfo.getPhone());
                coupon.setActivityId(actId);
                boolean save = couponService.save(coupon);
                if(!save) {
                    throw new CommonException("同步优惠券信息异常");
                }
            });
        }
    }

}
